﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using MagicControls.Utilities;

namespace MagicControls
{
    public enum ScrollBarType
    {
        Vertical,
        Horizontal
    }

    public class ScrollBar : ControlBase
    {
        #region Child controls

        private CanvasBase down;
        private CanvasBase thumb;
        private CanvasBase track;
        private CanvasBase up;

        #endregion

        #region Fields

        private Point clickedPoint;
        private bool mousePressedOnDown = false;
        private bool mousePressedOnThumb = false;
        private bool mousePressedOnUp = false;

        #endregion

        #region Property fields

        private double maximum = 0;
        private double minimum = 0;
        private ScrollBarType scrollBarType = MagicControls.ScrollBarType.Vertical;
        private double step = 1;
        private double value = 0;

        #endregion

        public ScrollBar()
        {
            Loaded += ScrollBar_Loaded;
        }

        protected override string ResourceName
        {
            get { return "MagicControls.XAML.ScrollBar.xaml"; }
        }

        #region Properties

        public string ScrollBarType
        {
            get { return scrollBarType.ToString(); }
            set { scrollBarType = (ScrollBarType) Enum.Parse(typeof (ScrollBarType), value, true); }
        }

        public double Minimum
        {
            get { return minimum; }
            set { minimum = value; }
        }

        public double Maximum
        {
            get { return maximum; }
            set { maximum = value; }
        }

        public double Step
        {
            get { return step; }
            set { step = value; }
        }

        public double Value
        {
            get { return value; }
            set
            {
                this.value = value;
                if (ValueChanged != null)
                {
                    ValueChanged(this, EventArgs.Empty);
                }
            }
        }

        public event EventHandler ValueChanged;

        #endregion

        #region Event handlers

        /// <summary>
        /// Initialize child control references and attach 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScrollBar_Loaded(object sender, EventArgs e)
        {
            SizeChanged += ScrollBar_SizeChanged;

            up = (CanvasBase) FindName("up");
            down = (CanvasBase) FindName("down");
            track = (CanvasBase) FindName("track");
            thumb = (CanvasBase) FindName("thumb");

            thumb.MouseLeftButtonDown += thumb_MouseLeftButtonDown;

            // Attaching mouse motion events to the top canvas
            FrameworkElement topElement = MCUtils.FindMainElement(this);
            topElement.MouseMove += thumb_MouseMove;
            topElement.MouseLeftButtonUp += thumb_MouseLeftButtonUp;
            topElement.MouseLeave += thumb_MouseLeave;
        }

        /// <summary>
        /// If mouse left parent region - stop thumb moving
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumb_MouseLeave(object sender, EventArgs e)
        {
            mousePressedOnThumb = false;
        }

        /// <summary>
        /// Moving mouse after clicking on thumb
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumb_MouseMove(object sender, MouseEventArgs e)
        {
            if (mousePressedOnThumb)
            {
                Point currentPosition = e.GetPosition(null);
                if (scrollBarType == MagicControls.ScrollBarType.Vertical)
                {
                    double deltaY = currentPosition.Y - clickedPoint.Y;
                    double newY = ((double) thumb.GetValue(Canvas.TopProperty)) + deltaY;

                    double newValue = GetValueFromThumbPosition(newY);

                    Value = newValue;
                    if (newValue != Minimum && newValue != Maximum)
                    {
                        thumb.SetValue(Canvas.TopProperty, newY);
                    }
                    else
                    {
                        SetThumbPosition();
                    }
                }
                else
                {
                    double deltaX = currentPosition.X - clickedPoint.X;
                    double newX = ((double) thumb.GetValue(Canvas.LeftProperty)) + deltaX;

                    double newValue = GetValueFromThumbPosition(newX);

                    Value = newValue;
                    if (newValue != Minimum && newValue != Maximum)
                    {
                        thumb.SetValue(Canvas.LeftProperty, newX);
                    }
                    else
                    {
                        SetThumbPosition();
                    }
                }
                clickedPoint = currentPosition;
            }
        }

        /// <summary>
        /// Mouse button is released
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumb_MouseLeftButtonUp(object sender, MouseEventArgs e)
        {
            mousePressedOnThumb = false;
        }

        /// <summary>
        /// If mouse pressed on thumb - set flag <code>mousePressedOnThumb</code> and set <code>clickedPoint</code>
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumb_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            mousePressedOnThumb = true;
            clickedPoint = e.GetPosition(null);
        }


        /// <summary>
        /// If control size is changed - change size of all children.
        /// </summary>
        /// <param name="o"></param>
        /// <param name="eventArg"></param>
        private void ScrollBar_SizeChanged(object o, SizeChangedEventArg eventArg)
        {
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
                UpdateVScroll();
            else
                UpdateHScroll();
        }

        #endregion

        #region Down button design

        internal void down_SizeChanged(object o, SizeChangedEventArg eventArg)
        {
            FrameworkElement MainRect = (FrameworkElement) FindName("down_MainRect");
            FrameworkElement GrayBorder = (FrameworkElement) FindName("down_GrayBorder");
            FrameworkElement BlackBorder = (FrameworkElement) FindName("down_BlackBorder");
            FrameworkElement WhiteBorder = (FrameworkElement) FindName("down_WhiteBorder");
            Polygon Arrow = (Polygon) FindName("down_Arrow");

            MainRect.Width = eventArg.Width - 3;
            MainRect.Height = eventArg.Height - 3;
            GrayBorder.Width = eventArg.Width - 2;
            GrayBorder.Height = eventArg.Height - 2;
            WhiteBorder.Width = eventArg.Width - 1;
            WhiteBorder.Height = eventArg.Height - 1;
            BlackBorder.Width = eventArg.Width;
            BlackBorder.Height = eventArg.Height;

            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                Point p1 = new Point();
                p1.X = GrayBorder.Width/2;
                p1.Y = GrayBorder.Height*4/5;

                Point p2 = new Point();
                p2.X = GrayBorder.Width/5;
                p2.Y = GrayBorder.Height/5;

                Point p3 = new Point();
                p3.X = GrayBorder.Width*4/5;
                p3.Y = GrayBorder.Height/5;

                Arrow.Points = new Point[] {p1, p2, p3, p1};
            }
            else
            {
                Point p1 = new Point();
                p1.Y = GrayBorder.Height/2;
                p1.X = GrayBorder.Width/5;

                Point p2 = new Point();
                p2.Y = GrayBorder.Height/5;
                p2.X = GrayBorder.Width*4/5;

                Point p3 = new Point();
                p3.Y = GrayBorder.Height*4/5;
                p3.X = GrayBorder.Width*4/5;

                Arrow.Points = new Point[] {p1, p2, p3, p1};
            }
        }

        internal void down_MainRect_MouseLeftButtonDown(object o, EventArgs arg)
        {
            Storyboard sb = (Storyboard) FindName("down_ClickButton");
            sb.Begin();
            mousePressedOnDown = true;
            MoveThumbDown();
        }

        internal void down_MainRect_MouseLeftButtonUp(object o, EventArgs arg)
        {
            Storyboard sb = (Storyboard) FindName("down_ReturnButton");
            sb.Begin();
            mousePressedOnDown = false;
        }

        internal void down_MainRect_MouseLeave(object o, EventArgs arg)
        {
            mousePressedOnDown = false;
        }


        internal void thumb_ScrollAnimation_Down_Completed(object sender, EventArgs e)
        {
            if (mousePressedOnDown)
            {
                MoveThumbDown();
            }
        }

        private void MoveThumbDown()
        {
            Storyboard moveThumb;
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                moveThumb = (Storyboard) FindName("thumb_ScrollAnimation_Down");
                SetPixelStep(moveThumb, false);
            }
            else
            {
                moveThumb = (Storyboard)FindName("thumb_ScrollAnimation_Right");
                SetPixelStep(moveThumb, true);
            }
            moveThumb.Begin();
            Value = GetValueFromThumbPosition();
        }

        #endregion

        #region Up button design

        internal void up_SizeChanged(object o, SizeChangedEventArg eventArg)
        {
            FrameworkElement MainRect = (FrameworkElement) FindName("up_MainRect");
            FrameworkElement GrayBorder = (FrameworkElement) FindName("up_GrayBorder");
            FrameworkElement BlackBorder = (FrameworkElement) FindName("up_BlackBorder");
            FrameworkElement WhiteBorder = (FrameworkElement) FindName("up_WhiteBorder");
            Polygon Arrow = (Polygon) FindName("up_Arrow");

            MainRect.Width = eventArg.Width - 3;
            MainRect.Height = eventArg.Height - 3;
            GrayBorder.Width = eventArg.Width - 2;
            GrayBorder.Height = eventArg.Height - 2;
            WhiteBorder.Width = eventArg.Width - 1;
            WhiteBorder.Height = eventArg.Height - 1;
            BlackBorder.Width = eventArg.Width;
            BlackBorder.Height = eventArg.Height;

            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                Point p1 = new Point();
                p1.X = GrayBorder.Width/2;
                p1.Y = GrayBorder.Height/5;

                Point p2 = new Point();
                p2.X = GrayBorder.Width*4/5;
                p2.Y = GrayBorder.Height*4/5;

                Point p3 = new Point();
                p3.X = GrayBorder.Width/5;
                p3.Y = GrayBorder.Height*4/5;

                Arrow.Points = new Point[] {p1, p2, p3, p1};
            }
            else
            {
                Point p1 = new Point();
                p1.Y = GrayBorder.Height/2;
                p1.X = GrayBorder.Width*4/5;

                Point p2 = new Point();
                p2.Y = GrayBorder.Height*4/5;
                p2.X = GrayBorder.Width/5;

                Point p3 = new Point();
                p3.Y = GrayBorder.Height/5;
                p3.X = GrayBorder.Width/5;

                Arrow.Points = new Point[] {p1, p2, p3, p1};
            }
        }

        internal void up_MainRect_MouseLeftButtonDown(object o, EventArgs arg)
        {
            Storyboard sb = (Storyboard) FindName("up_ClickButton");
            sb.Begin();
            mousePressedOnUp = true;
            MoveThumbUp();
        }

        internal void up_MainRect_MouseLeftButtonUp(object o, EventArgs arg)
        {
            Storyboard sb = (Storyboard) FindName("up_ReturnButton");
            sb.Begin();
            mousePressedOnUp = false;
        }

        internal void up_MainRect_MouseLeave(object o, EventArgs arg)
        {
            mousePressedOnUp = false;
        }


        internal void thumb_ScrollAnimation_Up_Completed(object sender, EventArgs e)
        {
            if (mousePressedOnUp)
            {
                MoveThumbUp();
            }
        }

        private void MoveThumbUp()
        {
            Storyboard moveThumb;
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                moveThumb = (Storyboard)FindName("thumb_ScrollAnimation_Up");
                SetPixelStep(moveThumb, true);
            } 
            else
            {
                moveThumb = (Storyboard)FindName("thumb_ScrollAnimation_Left");
                SetPixelStep(moveThumb, false);
            }
            moveThumb.Begin();
            Value = GetValueFromThumbPosition();
        }

        #endregion

        #region Thumb button design

        internal void thumb_SizeChanged(object o, SizeChangedEventArg eventArg)
        {
            FrameworkElement MainRect = (FrameworkElement) FindName("thumb_MainRect");
            FrameworkElement GrayBorder = (FrameworkElement) FindName("thumb_GrayBorder");
            FrameworkElement BlackBorder = (FrameworkElement) FindName("thumb_BlackBorder");
            FrameworkElement WhiteBorder = (FrameworkElement) FindName("thumb_WhiteBorder");

            MainRect.Width = eventArg.Width - 3;
            MainRect.Height = eventArg.Height - 3;
            GrayBorder.Width = eventArg.Width - 2;
            GrayBorder.Height = eventArg.Height - 2;
            WhiteBorder.Width = eventArg.Width - 1;
            WhiteBorder.Height = eventArg.Height - 1;
            BlackBorder.Width = eventArg.Width;
            BlackBorder.Height = eventArg.Height;
        }

        #endregion

        #region Utilities

        private void SetPixelStep(TimelineGroup moveThumb, bool destination)
        {
            DoubleAnimation animation = moveThumb.Children[0] as DoubleAnimation;
            if (animation != null)
            {
                double newStep;


                double trackSize;
                double upSize;
                double thumbSize;
                double thumbStartPosition;

                if (scrollBarType == MagicControls.ScrollBarType.Vertical)
                {
                    trackSize = track.Height;
                    upSize = up.Height;
                    thumbSize = thumb.Height;
                    thumbStartPosition = (double) thumb.GetValue(Canvas.TopProperty);
                } else
                {
                    trackSize = track.Width;
                    upSize = up.Width;
                    thumbSize = thumb.Width;
                    thumbStartPosition = (double)thumb.GetValue(Canvas.LeftProperty);
                }

                if (destination) //true is Up or Left
                {
                    newStep = -1 * step * trackSize / (Maximum - Minimum);
                    if (newStep + thumbStartPosition < upSize)
                    {
                        newStep = upSize - thumbStartPosition;
                    }
                } 
                else
                {
                    newStep = step * trackSize / (Maximum - Minimum);
                    if (newStep + thumbStartPosition + thumbSize > upSize + trackSize)
                    {
                        newStep = (upSize + trackSize) - thumbStartPosition -
                                  thumbSize;
                    }
                }
                animation.By = new Nullable<double>(newStep);
            }
        }
        
        private void UpdateVScroll()
        {
            if (Height != 0 && Width != 0)
            {
                up.Height = Width;
                up.Width = Width;

                down.Height = Width;
                down.Width = Width;

                track.Height = Height - up.Height - down.Height;
                track.Width = Width;

                up.SetValue(Canvas.TopProperty, 0);
                up.SetValue(Canvas.LeftProperty, 0);

                track.SetValue(Canvas.TopProperty, up.Height);
                track.SetValue(Canvas.LeftProperty, 0);

                down.SetValue(Canvas.TopProperty, up.Height + track.Height);
                down.SetValue(Canvas.LeftProperty, 0);

                thumb.Width = Width;
                if ((Maximum - Minimum) != 0)
                {
                    double thumbSize = (track.Height*track.Height)/(Maximum - Minimum);

                    if (thumbSize < 10) thumbSize = 10;
                    if (thumbSize >= track.Height)
                        thumb.Visibility = Visibility.Collapsed;
                    else
                    {
                        thumb.Visibility = Visibility.Visible;
                        thumb.Height = thumbSize;

                        SetThumbPosition();
                    }
                }
                else
                {
                    thumb.Visibility = Visibility.Collapsed;
                }
            }
        }

        private void UpdateHScroll()
        {
            if (Height != 0 && Width != 0)
            {
                up.Width = Height;
                up.Height = Height;

                down.Width = Height;
                down.Height = Height;

                track.Width = Width - up.Width - down.Width;
                track.Height = Height;

                up.SetValue(Canvas.TopProperty, 0);
                up.SetValue(Canvas.LeftProperty, down.Width + track.Width);

                track.SetValue(Canvas.TopProperty, 0);
                track.SetValue(Canvas.LeftProperty, down.Width);

                down.SetValue(Canvas.TopProperty, 0);
                down.SetValue(Canvas.LeftProperty, 0);

                thumb.Height = Height;
                if ((Maximum - Minimum) != 0)
                {
                    double thumbSize = (track.Width*track.Width)/(Maximum - Minimum);

                    if (thumbSize < 10) thumbSize = 10;
                    if (thumbSize >= track.Width)
                        thumb.Visibility = Visibility.Collapsed;
                    else
                    {
                        thumb.Visibility = Visibility.Visible;
                        thumb.Width = thumbSize;

                        SetThumbPosition();
                    }
                }
                else
                {
                    thumb.Visibility = Visibility.Collapsed;
                }
            }
        }

        /// <summary>
        /// Set thumb position by Value
        /// </summary>
        private void SetThumbPosition()
        {
            double thumbSize;
            double thumbStartPosition;
            double trackSize;
            double startPosition;
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                thumbSize = thumb.Height;
                trackSize = track.Height;
                startPosition = up.Height;
            } 
            else
            {
                thumbSize = thumb.Width;
                trackSize = track.Width;
                startPosition = down.Width;
            }
            thumbStartPosition = (trackSize - thumbSize) * (Value - Minimum) / (Maximum - Minimum) + startPosition;
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                thumb.SetValue(Canvas.TopProperty, thumbStartPosition);
                thumb.SetValue(Canvas.LeftProperty, 0);
            }
            else
            {
                thumb.SetValue(Canvas.TopProperty, 0);
                thumb.SetValue(Canvas.LeftProperty, thumbStartPosition);
            }
        }

        /// <summary>
        /// Get Value from current thumb position
        /// </summary>
        /// <returns></returns>
        private double GetValueFromThumbPosition()
        {
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                return GetValueFromThumbPosition((double) thumb.GetValue(Canvas.TopProperty));
            }
            else
            {
                return GetValueFromThumbPosition((double) thumb.GetValue(Canvas.LeftProperty));
            }
        }

        /// <summary>
        /// Get Value from specified thumb position
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        private double GetValueFromThumbPosition(double position)
        {
            double newValue = 0;
            if (scrollBarType == MagicControls.ScrollBarType.Vertical)
            {
                double thumbSize = thumb.Height;
                newValue = (
                               (position - up.Height)
                               /(track.Height - thumbSize)
                           )*(Maximum - Minimum) + Minimum;
            }
            else
            {
                double thumbSize = thumb.Width;
                newValue = (
                               (position - down.Width)
                               /(track.Width - thumbSize)
                           )*(Maximum - Minimum) + Minimum;
            }
            if (newValue < Minimum) newValue = Minimum;
            if (newValue > Maximum) newValue = Maximum;

            return newValue;
        }

        #endregion
    }
}